﻿using System.Net;
using ChatClient.Models;
using Microsoft.AspNetCore.SignalR.Client;
using Prism.Ioc;

namespace ChatClient.Services
{
    public class ChatService : IChatService
    {
        public event Action<string> ParticipantConnected = (_) => { };
        public event Action<string> ParticipantDisconnected = (_) => { };
        public event Action<User> ParticipantLoggedIn = (_) => { };
        public event Action<string> ParticipantLoggedOut = (_) => { };
        public event Action<string> ParticipantTyping = (_) => { };
        public event Action<string, string, MessageType> NewTextMessage = (_, _, _) => { };
        public event Action<string, byte[], MessageType> NewImageMessage = (_, _, _) => { };
        public event Action ConnectionReconnecting = () => { };
        public event Action ConnectionReconnected = () => { };
        public event Action ConnectionClosed = () => { };

        private string? UserId => _container.Resolve<ISessionService>().UserName;

        private HubConnection? _hubConnection;
        //private static readonly string _url = "http://localhost:8081/signalchat";

        private readonly IConfigService _configService;
        private readonly IContainerExtension _container;

        public ChatService(
            IConfigService configService,
            IContainerExtension container
        )
        {
            _configService = configService;
            _container = container;
        }

        public async Task ConnectAsync()
        {
            _hubConnection = new HubConnectionBuilder()
                .WithUrl(_configService.ServerUrl)
                .WithAutomaticReconnect()
                .Build();
            _hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);

            _hubConnection.On<string>("ParticipantConnected", (n) => ParticipantConnected.Invoke(n));
            _hubConnection.On<string>("ParticipantDisconnected", (n) => ParticipantDisconnected.Invoke(n));
            _hubConnection.On<User>("ParticipantLogin", (u) => ParticipantLoggedIn.Invoke(u));
            _hubConnection.On<string>("ParticipantLogout", (n) => ParticipantLoggedOut.Invoke(n));
            _hubConnection.On<string>("ParticipantTyping", (p) => ParticipantTyping.Invoke(p));
            _hubConnection.On<string, string>("BroadcastTextMessage",
                (n, m) => NewTextMessage.Invoke(n, m, MessageType.Broadcast));
            _hubConnection.On<string, byte[]>("BroadcastImageMessage",
                (n, m) => NewImageMessage.Invoke(n, m, MessageType.Broadcast));
            _hubConnection.On<string, string>("UniCastTextMessage",
                (n, m) => NewTextMessage.Invoke(n, m, MessageType.UniCast));
            _hubConnection.On<string, byte[]>("UniCastImageMessage",
                (n, m) => NewImageMessage.Invoke(n, m, MessageType.UniCast));

            _hubConnection.Reconnecting += OnHubConnectionReconnectingAsync;
            _hubConnection.Reconnected += OnHubConnectionReconnectedAsync;
            _hubConnection.Closed += OnHubConnectionClosedAsync;

            ServicePointManager.DefaultConnectionLimit = 10;
            await _hubConnection.StartAsync();
        }

        public async Task Disconnect()
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await LogoutAsync();
                await _hubConnection.StopAsync();
            }

            if (_hubConnection != null)
            {
                await _hubConnection.DisposeAsync();
            }

            _hubConnection = null;
        }

        private async Task OnHubConnectionReconnectingAsync(Exception? e)
        {
            await Task.CompletedTask;
            ConnectionReconnecting.Invoke();
        }

        private async Task OnHubConnectionReconnectedAsync(string? message)
        {
            await Task.CompletedTask;
            ConnectionReconnected.Invoke();
        }

        private async Task OnHubConnectionClosedAsync(Exception? e)
        {
            await Task.CompletedTask;
            ConnectionClosed.Invoke();
        }

        public async Task<Tuple<bool, List<User>>> LoginAsync(string name, byte[] photo)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                return await _hubConnection.InvokeAsync<Tuple<bool, List<User>>>("Login", name, photo);
            }

            return new Tuple<bool, List<User>>(false, new List<User>());
        }

        public async Task LogoutAsync()
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("Logout", UserId);
            }
        }

        public async Task SendBroadcastMessageAsync(string msg)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("BroadCastTextMessage", UserId, msg);
            }
        }

        public async Task SendBroadcastMessageAsync(byte[] img)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("BroadCastImageMessage", UserId, img);
            }
        }

        public async Task SendUniCastMessageAsync(string recipient, string msg)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("UniCastTextMessage", UserId, recipient, msg);
            }
        }

        public async Task SendUniCastMessageAsync(string recipient, byte[] img)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("UniCastImageMessage", UserId, recipient, img);
            }
        }

        public async Task TypingAsync(string recipient)
        {
            if (_hubConnection is { State: HubConnectionState.Connected })
            {
                await _hubConnection.InvokeAsync("Typing", UserId, recipient);
            }
        }
    }
}
